ECE 5725 Final Project Report
By Mei-Jen Lee(ml2298), Ziyin Wang(zw252).
In this project, we designed a dance box that enables a user to fit into the box shown on the PiTFT screen. We used a Pi camera to capture body movements and lit a number of boxes, each lasting for several seconds, and we set different difficulty levels for users to play. A song is played in the background as the game starts and the score is displayed.
We installed a Raspberry Pi Camera through the CSI port and used the ‘raspistill’ command to take a picture. At first, we encountered an issue with the camera, which shows the message camera is not enabled in this build. Then, we solved this issue by using ‘sudo raspi-config’ to enable the camera. We also encountered an issue where the camera was not detected. We tried to reinstall the camera and reboot the system, after that the camera could be detected successfully. To figure out the suitable method of Pi camera, we tested three approaches, VideoCapture, WebcamVideoStream, and PiVideoStream. The results show that VideoCapture is the slowest, PiVideoStream has some loss of accuracy but it is the fastest. For a better user experience, we made a tradeoff and chose PiVideoStream.
We detected the position of the bounding box using haar feature-based cascade classifier (haarcascade_fullbody.xml) in the opencv. After loading the classifier, we called the detectMultiScale() function and adjusted the parameters to make the result more accurate. We mainly considered parameters of minNeighbor, scaleFactor and minSize. MinNeighbor defines the minimum number of neighbors that each target should be detected to be considered as a real target, we set it to 1 in this project. ScaleFactor represents the proportion of reduction of each image size, we made a tradeoff as it influences speed and accuracy. MinSize indicates the limitation of object size, we avoided detecting impossible body objects that are too short or too thin.
We loaded a background music file, then called pygame.mixer.music.play() to play the music. The first argument loops determines how many times the music should be repeated, the second argument start indicates the time that the music begins to play, we set it to 0.0 so the music file should be played from the beginning. To stop playing the music, we used pygame.mixer.music.stop().
The coordinates are shown in Figure 1. The blue frame is the initial adjustment frame, the pink frame is the body object.
We displayed the camera frames on the PiTFT screen through pygame. The frames appeared inverted initially, so we used numpy.fliplr() and rot90() to reverse the order and rotate the array by 90 degrees. We used cv2.cvtColor() to convert the BGR color to RGB to fit pygame’s requirement. Then, we made a new surface and showed it on the screen.
This is the structure of the whole program, we defined 7 levels as shown in Figure 2. We set 4 physical buttons on PiTFT. 17, 22, 23, 27 represent for start, restart, score, quit respectively.
Level 1 | Show the game title and wait until game start |
Level 2 | Select difficulty |
Level 3 | Body calibration |
Level 4 | Present “Ready…” for 1 sec |
Level 5 | Present “Go!” for 1 sec |
Level 6 | Game (two difficulty levels) |
Level 7 | Show score |
This is the structure of the second layer FSM in level 3. Players can adjust their standing positions in this process.
State 0 | Draw red frame Go to state 1 if detected in the frame |
State 1 | Draw orange frame Wait for 0.9 sec and go to state 2 if detected in the frame more than 10 times in 0.9 sec Else go back to state 0 |
State 2 | Draw yellow frame Wait for 0.9 sec and go to state 3 if detected in the frame more than 10 times in 0.9 sec Else go back to state 0 |
State 3 | Draw green frame Wait for 0.9 sec and go to level 3 if detected in the frame more than 10 times in 0.9 sec Else go back to state 0 |
We generated initial random numbers for random frame assignment in state 3 because we want to avoid multiple generate before getting points. In each state, we have to record the start time when the state is about to change (like the transition line in the picture for it only happens once) instead of recording the start time in the beginning of the next state, because we don’t want to update start time again and again.
This is the structure of the second layer FSM in level 6.
State 0 | Draw red frame Go to state 1 if detected in the frame |
State 1 | Draw yellow frame Wait for 0.9 sec and go to state 2 if detected in the frame more than 10 times in 0.9 sec Else go back to state 0 |
State 2 | Display “Good!” in green Wait for 0.9 sec and get 1 point if detected in the frame more than 10 times in 0.8 sec Else go back to state 0 |
In the hard mode, we shrunk the box size to increase the difficulty during the game. Each time, the width decreases by 3 pixels and the height decreases by 2 pixels. We set a minimum width and height to avoid unlimited shrinking.
We generated a coin and a mosquito in the game. The player would get bonus points if grabs the coin and would lose some points if the mosquito catches up with him. To define whether the mosquito hits the player, we used the center coordinates of the mosquito and calculated if the object is inside the box of the player. The mosquito keeps track of the player through the center coordinates, the length of vector between mosquito and player would cause high speed, so we used unit vector to update the coordinates of mosquito.
In this project, we realized all the functionalities in the game as planned. We spent some time debugging the problems, and we solved all the problems successfully. We installed a Pi camera, and compared three methods to figure out the fastest one. We detected the body object and considered the impact of different parameters to get a more accurate result. We implemented the 7 layer FSM as the main structure of the game, and in the second layer, we have calibration FSM and game FSM. We provided two difficulty levels of the game, in the hard mode, the difficulty can be adjusted automatically.
In conclusion, we designed a dance box game. Users can fit into the box that is shown on the PiTFT screen. A background music is played in the game, and the remaining time of the game is dependent on the music and is shown on the screen. We used a Pi camera to capture body movements and displayed boxes on the screen, each box lasting for 5 seconds. When the timer runs out, the program will calculate the scores by counting the number of keypoints of the human body inside the box. We designed an algorithm to calculate the positions of the boxes and drew them on the video output of the PiTFT. The boxes appear on the screen randomly, and we added the requirement that the places be low and large enough for players to fit in. We created a user interface that allows users to select different difficulty levels and start the game. We added bonus points and obstacles that show on the screen for a limited time for players to collect and avoid. When a user receives higher/lower scores, the level is automatically adjusted up/down.
We plan to add some new rules for the game. For example, each player will have 3 lives, when a player gets a score lower than 20, he will lose one life and start a new round. When all 3 lives are lost, the game ends. We could also support multiple players, and record the highest score of each user.
ml2298@cornell.edu
Designed the overall software architecture.
zw252@cornell.edu
Tested the overall system.
import random import RPi.GPIO as GPIO import numpy as np import time import cv2 from imutils.video import WebcamVideoStream from imutils.video.pivideostream import PiVideoStream from pydub import AudioSegment from pydub.playback import play import threading import imutils import time import os import pygame body_classifier = cv2.CascadeClassifier('model/haarcascade_fullbody.xml') os.putenv('SDL_VIDEODRIVER','fbcon') os.putenv('SDL_FBDEV','/dev/fb1') os.putenv('SDL_MOUSEDRV','TSLIB') os.putenv('SDL_MOUSEDEV','/dev/input/touchscreen') TIMEOUT=240 START=False QUIT=False b1=17 b2=22 b3=23 b4=27 GPIO.setmode(GPIO.BCM) GPIO.setup(b1,GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(b2,GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(b3,GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(b4,GPIO.IN, pull_up_down=GPIO.PUD_UP) CODERUN=True LEVEL="level1" REMAIN_TIME=31 pygame.init() pygame.display.init() pygame.mouse.set_visible(True) #color definition black=(0,0,0) COLOR=black white=(255,255,255) GREEN=(0,255,0) ORANGE=(0,126,255) RED=(0,0,255) YELLOW=(0,255,255) #PiTFT screen setting (width, height)=(320,240) size=(width,height) screen = pygame.display.set_mode(size) #Image load ball = pygame.image.load("picture/minion.jpg") ballrect=ball.get_rect(center=(160,120)) speed=[-1,-1] bug = pygame.image.load("picture/bug.png") bug = pygame.transform.scale(bug,(40,40)) bugrect=bug.get_rect(center=(100,100)) block = pygame.image.load("picture/block.jpeg") block = pygame.transform.scale(block,(50,50)) blockrect=block.get_rect(center=(100,100)) #font initialization EASY_POSITION=(120,100) HARD_POSITION=(120,170) myFont = pygame.font.SysFont("Times New Roman",30) myFont_small = pygame.font.SysFont("Times New Roman",22) difficulty_font = myFont.render("Select Difficulty:",20,white) easy_font = myFont.render("Easy",140,white) hard_font = myFont.render("Hard",140,white) easy = easy_font.get_rect(center=EASY_POSITION) hard = hard_font.get_rect(center=HARD_POSITION) frame=white COUNT_KEEP=0 TEXT="" KEEPSTATE=0 THRESHOLD=1 CONFIGURATION_TIME=0.25 GAMESTATE=0 BEAT=39 SCORE=0 THRESHOLD2=1 GAMELEVEL="level5" MODE_WIDTH=150 MODE_HEIGHT=230 MUSIC_LENGTH=32 WIDTH_LIMIT=90 HEIGHT_LIMIT=130 EASY_X_MAX=320-MODE_WIDTH EASY_BOTTOM_MIN=229 HARD_X_MAX=240-WIDTH_LIMIT-1 HARD_BOTTOM_MIN=238 STRIDE=45 DIFFICULTY="Easy" try: pygame.mixer.music.load('music/mii.mp3') pygame.mixer.music.play(-1,0.0) print("Setting camera") #cap = WebcamVideoStream(src=-1).start() cap = PiVideoStream().start() time.sleep(1) STARTTIME=time.time() while CODERUN: #TIMEOUT NOW=time.time() if NOW-STARTTIME>TIMEOUT: print("time out") quit() #quit if not GPIO.input(b4): cap.stop() cv2.destroyAllWindows() quit() time.sleep(0.01) #WAIT UNTIL START if LEVEL=="level1": #select difficulty level if not GPIO.input(b1): pygame.mixer.music.load('music/mii.mp3') pygame.mixer.music.play(-1,0.0) LEVEL="level1.5" font1 = myFont.render("press button 17 to start",50,(0,0,0)) font2 = myFont.render("Dance Box",120,(0,0,0)) screen.fill(black) screen.blit(ball,ballrect) screen.blit(font1,(20,200)) screen.blit(font2,(100,60)) pygame.display.flip() #select difficulty elif LEVEL=="level1.5": SCORE=0 for event in pygame.event.get(): #easy if event.pos[0]<easy.right and event.pos[0]>easy.left and event.pos[1]>easy.top and event.pos[1]<easy.bottom: print("select easy") MODE_WIDTH=150 MODE_HEIGHT=230 X_MAX=EASY_X_MAX BOTTOM_MIN=EASY_BOTTOM_MIN GAMELEVEL="level5" DIFFICULTY="Easy" LEVEL="level2" #hard if event.pos[0]<hard.right and event.pos[0]>hard.left and event.pos[1]>hard.top and event.pos[1]<hard.bottom: print("select hard") MODE_WIDTH=150 MODE_HEIGHT=230 X_MAX=HARD_X_MAX BOTTOM_MIN=HARD_BOTTOM_MIN GAMELEVEL="level5.5" DIFFICULTY="Hard" LEVEL="level2" screen.fill(black) screen.blit(difficulty_font,(50,40)) screen.blit(easy_font,EASY_POSITION) screen.blit(hard_font,HARD_POSITION) pygame.display.flip() #CAMERA CALIBRATION elif LEVEL=="level2": #select difficulty level #read from camera frame = cap.read() #preprocessing for body detection frame = imutils.resize(frame,width=320) frame = cv2.flip(frame,1) gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #body detection bodies = body_classifier.detectMultiScale(frame,scaleFactor=1.009,minNeighbors=1,minSize=(60,110)) x,y,w,h=0,0,0,0 if len(bodies)>0: (x,y,w,h) = bodies[0] COLOR=RED #COUNT STAY TIME if KEEPSTATE==0: COUNT_KEEP=0 TEXT="" if (x>100 and y>1 and x+w<220 and y+h<239): KEEPSTATE=1 KEEP_START=time.time() elif KEEPSTATE==1: TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL<CONFIGURATION_TIME: TEXT="3" if (x>100 and y>1 and x+w<220 and y+h<239): COUNT_KEEP+=1 COLOR=ORANGE else: if COUNT_KEEP>=THRESHOLD: KEEPSTATE=2 COUNT_KEEP=0 KEEP_START=time.time() else: KEEPSTATE=0 elif KEEPSTATE==2: TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL<CONFIGURATION_TIME: TEXT="2" if (x>100 and y>1 and x+w<220 and y+h<239): COUNT_KEEP+=1 COLOR=YELLOW else: if COUNT_KEEP>=THRESHOLD: KEEPSTATE=3 COUNT_KEEP=0 KEEP_START=time.time() else: KEEPSTATE=0 elif KEEPSTATE==3: TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL<CONFIGURATION_TIME: TEXT="1" if (x>100 and y>1 and x+w<220 and y+h<239): COUNT_KEEP+=1 COLOR=GREEN else: if COUNT_KEEP>=THRESHOLD: COUNT_KEEP=0 KEEPSTATE=0 #start to count temple TEMPLE=0 LEVEL="level3" LEVEL3START=time.time() target_x = random.randrange(1, X_MAX, STRIDE) target_bottom = random.randrange(BOTTOM_MIN, 239, STRIDE) target_w = MODE_WIDTH target_h = MODE_HEIGHT pygame.mixer.music.stop() pygame.mixer.music.load('music/mario.mp3') pygame.mixer.music.play(0,0.0) else: KEEPSTATE=0 cv2.putText(frame,TEXT,(120,160),cv2.FONT_HERSHEY_DUPLEX, 5, COLOR,9) cv2.putText(frame,"Keep your body in the frame",(8,20),cv2.FONT_HERSHEY_DUPLEX, 0.6,COLOR,1) cv2.rectangle(frame,(100,1),(100+120,1+238),COLOR,2) frame = np.fliplr(frame) frame = np.rot90(frame) frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) surf = pygame.surfarray.make_surface(frame) screen.fill(black) screen.blit(surf,(0,0)) pygame.display.flip() #cv2.imshow('haha',frame) if cv2.waitKey(1)==13: break #GAME START! #font size elif LEVEL=="level3": if time.time()-LEVEL3START>1: LEVEL="level4" LEVEL4START=time.time() font1 = myFont.render("Ready...",20,black) font2 = myFont.render(DIFFICULTY+" Level",20,black) screen.fill(black) screen.blit(ball,ballrect) screen.blit(font1,(120,160)) screen.blit(font2,(80,40)) pygame.display.flip() elif LEVEL=="level4": if time.time()-LEVEL4START>1: LEVEL=GAMELEVEL MUSIC_START=time.time() font1 = myFont.render("Go",20,black) font2 = myFont.render(DIFFICULTY+" Level",20,black) screen.fill(black) screen.blit(ball,ballrect) screen.blit(font1,(130,160)) screen.blit(font2,(80,40)) pygame.display.flip() elif LEVEL=="level5": #select difficulty level if not GPIO.input(b1): pygame.mixer.music.load('music/mii.mp3') pygame.mixer.music.play(-1,0.0) LEVEL="level1" #restart if not GPIO.input(b2): pygame.mixer.music.load('music/mario.mp3') pygame.mixer.music.play(0,0.0) LEVEL="level3" #show score if not GPIO.input(b3): pygame.mixer.music.load('music/gameover.mp3') pygame.mixer.music.play(0,0.0) LEVEL="level6" game_time = time.time()-MUSIC_START REMAIN_TIME=int(31-game_time) if time.time()-MUSIC_START>31: LEVEL="level6" pygame.mixer.music.load('music/gameover.mp3') pygame.mixer.music.play(0,0.0) #read from camera frame = cap.read() #preprocessing for body detection frame = imutils.resize(frame,width=320) frame = cv2.flip(frame,1) gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #body detection bodies = body_classifier.detectMultiScale(frame,scaleFactor=1.009,minNeighbors=1,minSize=(60,110)) x,y,w,h=0,0,0,0 if len(bodies)>0: (x,y,w,h) = bodies[0] #for (x,y,w,h) in bodies: # cv2.rectangle(frame,(x,y),(x+w,y+h),COLOR,2) #block fly blockrect=blockrect.move(speed) if blockrect.left<0 or blockrect.right>width: speed[0]=-speed[0] if blockrect.top<0 or blockrect.bottom>height: speed[1]=-speed[1] BLOCK_POSITION_X = blockrect.center[0] BLOCK_POSITION_Y = blockrect.center[1] #Hit Bug if len(bodies)>0: if BLOCK_POSITION_X>x and BLOCK_POSITION_Y>y and BLOCK_POSITION_X<x+w and BLOCK_POSITION_Y<y+h: SCORE+=0.2 #text position POSITION=(120,160) #font size SIZE=5 #red #COUNT STAY TIME if KEEPSTATE==0: TEXT="0" COLOR=RED COUNT_KEEP=0 TEXT="" if (x>target_x and y>target_bottom-target_h and x+w<target_x+target_w and y+h<target_bottom): KEEPSTATE=1 KEEP_START=time.time() #WAIT FOR ONE SECOND elif KEEPSTATE==1: COLOR=YELLOW TEXT="Keep" TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL<CONFIGURATION_TIME: if (x>target_x and y>target_bottom-target_h and x+w<target_x+target_w and y+h<target_bottom): COUNT_KEEP+=1 else: if COUNT_KEEP>=THRESHOLD2: KEEPSTATE=2 COUNT_KEEP=0 KEEP_START=time.time() else: KEEPSTATE=0 #GET POINT! elif KEEPSTATE==2: #Green COLOR=GREEN TEXT="Good!" TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL>0.5: KEEPSTATE=0 SCORE+=10 target_x = random.randrange(1, X_MAX, STRIDE) target_bottom = random.randrange(BOTTOM_MIN, 239, STRIDE) target_w = MODE_WIDTH target_h = MODE_HEIGHT #Score cv2.putText(frame,"Score:"+str(int(SCORE)),(190,30),cv2.FONT_HERSHEY_DUPLEX, 0.8,(0,0,0),2) #Remain time cv2.putText(frame,"Time:"+str(REMAIN_TIME),(10,30),cv2.FONT_HERSHEY_DUPLEX, 0.8,(0,0,0),2) cv2.putText(frame,TEXT,(60,150),cv2.FONT_HERSHEY_DUPLEX, 2, COLOR,8) cv2.rectangle(frame,(target_x,target_bottom-target_h),(target_x+target_w,target_bottom),COLOR,2) frame = np.fliplr(frame) frame = np.rot90(frame) frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) surf = pygame.surfarray.make_surface(frame) screen.fill(black) screen.blit(surf,(0,0)) screen.blit(block,blockrect) pygame.display.flip() if cv2.waitKey(1)==13: break #hard elif LEVEL=="level5.5": #select difficulty level if not GPIO.input(b1): pygame.mixer.music.load('music/mii.mp3') pygame.mixer.music.play(-1,0.0) LEVEL="level1" #restart if not GPIO.input(b2): pygame.mixer.music.load('music/mario.mp3') pygame.mixer.music.play(0,0.0) LEVEL="level3" #show score if not GPIO.input(b3): pygame.mixer.music.load('music/gameover.mp3') pygame.mixer.music.play(0,0.0) LEVEL="level6" time.sleep(0.02) game_time = time.time()-MUSIC_START REMAIN_TIME=31-game_time if time.time()-MUSIC_START>31: LEVEL="level6" pygame.mixer.music.load('music/gameover.mp3') pygame.mixer.music.play(0,0.0) #body detection #read from camera frame = cap.read() #preprocessing for body detection frame = imutils.resize(frame,width=320) #have to flip for some reason frame = cv2.flip(frame,1) gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #body detection bodies = body_classifier.detectMultiScale(frame,scaleFactor=1.009,minNeighbors=1,minSize=(60,110)) x,y,w,h=0,0,0,0 if len(bodies)>0: (x,y,w,h) = bodies[0] #for (x,y,w,h) in bodies: #cv2.rectangle(frame,(x,y),(x+w,y+h),COLOR,2) BODY_CENTER = [x+w/2,y+h/2] #bug fly bugrect=bugrect.move(speed) BUG_POSITION_X = bugrect.center[0] BUG_POSITION_Y = bugrect.center[1] if BODY_CENTER[0]<BUG_POSITION_X: x_direction=-2 else: x_direction=2 if BODY_CENTER[1]<BUG_POSITION_Y: y_direction=-2 else: y_direction=2 if bugrect.left<0 or bugrect.right>width: speed[0]=-speed[0] if bugrect.top<0 or bugrect.bottom>height: speed[1]=-speed[1] #if no collision && user detected -> than chase users if len(bodies)>0 and bugrect.left>0 and bugrect.right<width and bugrect.top>0 and bugrect.bottom<height: speed=[x_direction,y_direction] #Hit Bug if len(bodies)>0: if BUG_POSITION_X>x and BUG_POSITION_Y>y and BUG_POSITION_X<x+w and BUG_POSITION_Y<y+h: if SCORE>0.5: SCORE-=0.5 #text position POSITION=(120,160) #font size SIZE=5 #COUNT STAY TIME if KEEPSTATE==0: TEXT="0" COLOR=RED COUNT_KEEP=0 TEXT="" if (x>target_x and y>target_bottom-target_h and x+w<target_x+target_w and y+h<target_bottom): KEEPSTATE=1 KEEP_START=time.time() #WAIT FOR ONE SECOND elif KEEPSTATE==1: COLOR=YELLOW TEXT="Keep" TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL<CONFIGURATION_TIME: if (x>target_x and y>target_bottom-target_h and x+w<target_x+target_w and y+h<target_bottom): COUNT_KEEP+=1 else: if COUNT_KEEP>=THRESHOLD2: KEEPSTATE=2 COUNT_KEEP=0 KEEP_START=time.time() else: KEEPSTATE=0 #GET POINT! elif KEEPSTATE==2: #Green COLOR=GREEN TEXT="Good!" POSITION=(70,150) SIZE=3 TIME_INTERVAL=time.time()-KEEP_START if TIME_INTERVAL>0.5: KEEPSTATE=0 SCORE+=10 target_x = random.randrange(1, X_MAX, STRIDE) target_bottom = random.randrange(BOTTOM_MIN, 239, STRIDE) target_w = MODE_WIDTH target_h = MODE_HEIGHT if MODE_WIDTH>WIDTH_LIMIT+6: MODE_WIDTH-=3 if MODE_HEIGHT>HEIGHT_LIMIT+2: MODE_HEIGHT-=2 cv2.putText(frame,"Score:"+str(int(SCORE)),(190,30),cv2.FONT_HERSHEY_DUPLEX, 0.8,(0,0,0),2) #Remain time cv2.putText(frame,"Time:"+str(int(REMAIN_TIME)),(10,30),cv2.FONT_HERSHEY_DUPLEX, 0.8,(0,0,0),2) cv2.putText(frame,TEXT,(60,150),cv2.FONT_HERSHEY_DUPLEX, 2, COLOR,8) cv2.rectangle(frame,(target_x,target_bottom-target_h),(target_x+target_w,target_bottom),COLOR,2) frame = np.fliplr(frame) frame = np.rot90(frame) frame = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB) surf = pygame.surfarray.make_surface(frame) screen.fill(black) screen.blit(surf,(0,0)) screen.blit(bug,bugrect) pygame.display.flip() if cv2.waitKey(1)==13: break #Show score elif LEVEL=="level6": for event in pygame.event.get(): if event.type==pygame.MOUSEBUTTONDOWN and event.pos[0]<320 and event.pos[0]>0 and event.pos[1]>0 and event.pos[1]<240: pygame.mixer.music.load('music/mii.mp3') pygame.mixer.music.play(-1,0.0) LEVEL="level1" #select difficulty level #if not GPIO.input(b1): # pygame.mixer.music.load('music/mii.mp3') # pygame.mixer.music.play(-1,0.0) # LEVEL="level1" #restart if not GPIO.input(b2): pygame.mixer.music.load('music/mario.mp3') pygame.mixer.music.play(0,0.0) LEVEL="level3" font1 = myFont_small.render("press the screen to play again",30,(0,0,0)) font2 = myFont_small.render("press button 22 to quit",30,(0,0,0)) font3 = myFont.render("Score:"+str(int(SCORE)),100,(0,0,0)) font4 = myFont.render(DIFFICULTY+" Level",50,black) screen.fill(black) screen.blit(ball,ballrect) screen.blit(font1,(20,170)) screen.blit(font2,(20,200)) screen.blit(font3,(100,60)) screen.blit(font4,(100,30)) pygame.display.flip() except KeyboardInterrupt: cap.stop() cv2.destroyAllWindows() quit() except: cap.stop() cv2.destroyAllWindows() quit() finally: cap.stop() cv2.destroyAllWindows()